package protocol

import (
	"encoding/binary"
	"errors"
	"fmt"
	"math"
	"net"
	"strconv"

	"alarm_protocol/common"
)

const (
	FixedComponentAddr uint32 = 1    // 固定的部件地址 01 00 00 00
	FixedTypeFlag      byte   = 0x03 // 固定的类型标志 0x82
)

// RemoteOperationData 远程操作数据格式
type RemoteOperationData struct {
	TypeFlag      byte   // 类型标志（1 字节）
	ComponentAddr uint32 // 部件地址（4 字节）
	OperationType byte   // 操作类型（1 字节）
	InfoBody      []byte // 信息体（变长）
}

// AnalogChannelSettings 模拟量通道设置
type AnalogChannelSettings struct {
	AlarmEnable       byte    // 告警使能（1 字节）
	UpperLimit        float32 // 上限阈值（4 字节）
	UpperLimitRelease float32 // 上限解除阈值（4 字节）
	LowerLimit        float32 // 下限阈值（4 字节）
	LowerLimitRelease float32 // 下限解除阈值（4 字节）
	FluctuationLimit  float32 // 波动阈值（4 字节）
}

// TimeIntervalSettings 上报、采集时间间隔设置
type TimeIntervalSettings struct {
	ReportInterval  uint32 // 上报间隔（4 字节）
	CollectInterval uint32 // 采集间隔（4 字节）
}

// IPPortSettings IP和端口号设置
type IPPortSettings struct {
	IP   net.IP // IP地址（4字节）
	Port uint16 // 端口号（2字节）
}

// GenerateRemoteOperationData 根据Command生成远程操作数据
func GenerateRemoteOperationData(cmd common.Command) ([]byte, error) {
	switch cmd.Method {
	case "AnalogChannelSettings":
		return handleAnalogChannelSettings(cmd.Params)
	case "TimeIntervalSettings":
		return handleTimeIntervalSettings(cmd.Params)
	case "IPPortSettings":
		return handleIPPortSettings(cmd.Params)
	default:
		return nil, fmt.Errorf("unsupported method: %s", cmd.Method)
	}
}

func handleAnalogChannelSettings(params map[string]interface{}) ([]byte, error) {
	settings := AnalogChannelSettings{}

	alarmEnable, err := getByteParam(params, "AlarmEnable")
	if err != nil {
		return nil, err
	}
	settings.AlarmEnable = alarmEnable

	upperLimit, err := getFloat32Param(params, "UpperLimit")
	if err != nil {
		return nil, err
	}
	settings.UpperLimit = upperLimit

	upperLimitRelease, err := getFloat32Param(params, "UpperLimitRelease")
	if err != nil {
		return nil, err
	}
	settings.UpperLimitRelease = upperLimitRelease

	lowerLimit, err := getFloat32Param(params, "LowerLimit")
	if err != nil {
		return nil, err
	}
	settings.LowerLimit = lowerLimit

	lowerLimitRelease, err := getFloat32Param(params, "LowerLimitRelease")
	if err != nil {
		return nil, err
	}
	settings.LowerLimitRelease = lowerLimitRelease

	fluctuationLimit, err := getFloat32Param(params, "FluctuationLimit")
	if err != nil {
		return nil, err
	}
	settings.FluctuationLimit = fluctuationLimit

	return SetAnalogChannel(settings), nil
}

func handleTimeIntervalSettings(params map[string]interface{}) ([]byte, error) {
	settings := TimeIntervalSettings{}

	reportInterval, err := getUint32Param(params, "ReportInterval")
	if err != nil {
		return nil, err
	}
	settings.ReportInterval = reportInterval

	collectInterval, err := getUint32Param(params, "CollectInterval")
	if err != nil {
		return nil, err
	}
	settings.CollectInterval = collectInterval

	return SetTimeIntervals(settings), nil
}

func handleIPPortSettings(params map[string]interface{}) ([]byte, error) {
	settings := IPPortSettings{}

	ipStr, err := getStringParam(params, "IP")
	if err != nil {
		return nil, err
	}
	settings.IP = net.ParseIP(ipStr)
	if settings.IP == nil {
		return nil, errors.New("invalid IP address")
	}

	port, err := getUint16Param(params, "Port")
	if err != nil {
		return nil, err
	}
	settings.Port = port

	return SetIPPort(settings), nil
}

func getByteParam(params map[string]interface{}, key string) (byte, error) {
	value, ok := params[key]
	if !ok {
		return 0, errors.New("parameter not found: " + key)
	}

	switch v := value.(type) {
	case float64:
		return byte(v), nil
	case string:
		i, err := strconv.Atoi(v)
		if err != nil {
			return 0, errors.New("invalid value for parameter " + key + ": " + v)
		}
		return byte(i), nil
	default:
		return 0, errors.New("invalid type for parameter " + key)
	}
}

func getFloat32Param(params map[string]interface{}, key string) (float32, error) {
	value, ok := params[key]
	if !ok {
		return 0, errors.New("parameter not found: " + key)
	}

	switch v := value.(type) {
	case float64:
		return float32(v), nil
	case string:
		f, err := strconv.ParseFloat(v, 32)
		if err != nil {
			return 0, errors.New("invalid value for parameter " + key + ": " + v)
		}
		return float32(f), nil
	default:
		return 0, errors.New("invalid type for parameter " + key)
	}
}

func getUint32Param(params map[string]interface{}, key string) (uint32, error) {
	value, ok := params[key]
	if !ok {
		return 0, errors.New("parameter not found: " + key)
	}

	switch v := value.(type) {
	case float64:
		return uint32(v), nil
	case string:
		i, err := strconv.Atoi(v)
		if err != nil {
			return 0, errors.New("invalid value for parameter " + key + ": " + v)
		}
		return uint32(i), nil
	default:
		return 0, errors.New("invalid type for parameter " + key)
	}
}

func getUint16Param(params map[string]interface{}, key string) (uint16, error) {
	value, ok := params[key]
	if !ok {
		return 0, errors.New("parameter not found: " + key)
	}

	switch v := value.(type) {
	case float64:
		return uint16(v), nil
	case string:
		i, err := strconv.Atoi(v)
		if err != nil {
			return 0, errors.New("invalid value for parameter " + key + ": " + v)
		}
		return uint16(i), nil
	default:
		return 0, errors.New("invalid type for parameter " + key)
	}
}

func getStringParam(params map[string]interface{}, key string) (string, error) {
	value, ok := params[key]
	if !ok {
		return "", errors.New("parameter not found: " + key)
	}

	if v, ok := value.(string); ok {
		return v, nil
	}

	return "", errors.New("invalid type for parameter " + key)
}

// SetAnalogChannel 设置模拟量通道
func SetAnalogChannel(settings AnalogChannelSettings) []byte {
	data := RemoteOperationData{
		TypeFlag:      FixedTypeFlag,
		ComponentAddr: FixedComponentAddr,
		OperationType: 0x40, // 64 对应于设置模拟量通道
		InfoBody:      make([]byte, 21),
	}

	data.InfoBody[0] = settings.AlarmEnable
	binary.LittleEndian.PutUint32(data.InfoBody[1:5], math.Float32bits(settings.UpperLimit))
	binary.LittleEndian.PutUint32(data.InfoBody[5:9], math.Float32bits(settings.UpperLimitRelease))
	binary.LittleEndian.PutUint32(data.InfoBody[9:13], math.Float32bits(settings.LowerLimit))
	binary.LittleEndian.PutUint32(data.InfoBody[13:17], math.Float32bits(settings.LowerLimitRelease))
	binary.LittleEndian.PutUint32(data.InfoBody[17:21], math.Float32bits(settings.FluctuationLimit))

	return encodeRemoteOperationData(data)
}

// SetTimeIntervals 设置上报、采集时间间隔
func SetTimeIntervals(settings TimeIntervalSettings) []byte {
	data := RemoteOperationData{
		TypeFlag:      FixedTypeFlag,
		ComponentAddr: FixedComponentAddr,
		OperationType: 0x80, // 128 对应于设置上报、采集时间间隔
		InfoBody:      make([]byte, 8),
	}

	binary.LittleEndian.PutUint32(data.InfoBody[0:4], settings.ReportInterval)
	binary.LittleEndian.PutUint32(data.InfoBody[4:8], settings.CollectInterval)

	return encodeRemoteOperationData(data)
}

// SetIPPort 设置IP和端口号
func SetIPPort(settings IPPortSettings) []byte {
	data := RemoteOperationData{
		TypeFlag:      FixedTypeFlag,
		ComponentAddr: FixedComponentAddr,
		OperationType: 0x20, // 32 对应于设置IP和端口号
		InfoBody:      make([]byte, 6),
	}

	copy(data.InfoBody[0:4], settings.IP.To4())
	binary.LittleEndian.PutUint16(data.InfoBody[4:6], settings.Port)

	return encodeRemoteOperationData(data)
}

// encodeRemoteOperationData 编码远程操作数据
func encodeRemoteOperationData(data RemoteOperationData) []byte {
	result := make([]byte, 6+len(data.InfoBody))
	result[0] = data.TypeFlag
	binary.LittleEndian.PutUint32(result[1:5], data.ComponentAddr)
	result[5] = data.OperationType
	copy(result[6:], data.InfoBody)
	return result
}
